#include <iostream>

#include <windows.h>

/* log */
#include "minilog_static.hxx"

#include "can_api.h"

#ifdef __cplusplus
extern "C" {
#endif

    // C code...
#include <stdio.h>
#include "ControlCAN.h"

#ifdef __cplusplus
}
#endif



const int devtype = VCI_USBCAN2;//设备类型号
int devind = CAN_BOX0; //设备索引号，如果接了俩个CAN盒子，那么就有2个devind，一个0，一个1

#if USING_MSVC_BUILD
HMODULE module = NULL;

DWORD(*VCI_OpenDevice)(DWORD DeviceType, DWORD DeviceInd, DWORD Reserved);
DWORD(*VCI_CloseDevice)(DWORD DeviceType, DWORD DeviceInd);
DWORD(*VCI_InitCAN)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_INIT_CONFIG pInitConfig);

DWORD(*VCI_ReadBoardInfo)(DWORD DeviceType, DWORD DeviceInd, PVCI_BOARD_INFO pInfo);


DWORD(*VCI_SetReference)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, DWORD RefType, void* pData);

ULONG(*VCI_GetReceiveNum)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd);
DWORD(*VCI_ClearBuffer)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd);

DWORD(*VCI_StartCAN)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd);
DWORD(*VCI_ResetCAN)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd);

ULONG(*VCI_Transmit)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_CAN_OBJ pSend, ULONG Len);
ULONG(*VCI_Receive)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_CAN_OBJ pReceive, ULONG Len, INT WaitTime);
#endif

int init_dll(void)
{
#if USING_MSVC_BUILD
    static int flag = 0;
    char* p = (char*)"no";

    module = LoadLibraryA("ControlCAN.dll");

    if (module == NULL)
    {
        printf("WHAT THE FUCK when load dll, fuck Microsoft\n");
        return STATUS_ERR;
    }
    if (flag == 0)
    {
        flag = 1;

        VCI_OpenDevice = (DWORD(*) (DWORD DeviceType, DWORD DeviceInd, DWORD Reserved)) GetProcAddress(module, "VCI_OpenDevice");
        VCI_CloseDevice = (DWORD(*) (DWORD DeviceType, DWORD DeviceInd))GetProcAddress(module, "VCI_CloseDevice");
        VCI_InitCAN = (DWORD(*)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_INIT_CONFIG pInitConfig))GetProcAddress(module, "VCI_InitCAN");
        VCI_StartCAN = (DWORD(*)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd))GetProcAddress(module, "VCI_StartCAN");
        VCI_ClearBuffer = (DWORD(*)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd))GetProcAddress(module, "VCI_ClearBuffer");
        VCI_Transmit = (ULONG(*)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_CAN_OBJ pSend, ULONG Len))GetProcAddress(module, "VCI_Transmit");
        VCI_Receive = (ULONG(*)(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, PVCI_CAN_OBJ pReceive, ULONG Len, INT WaitTime) )GetProcAddress(module, "VCI_Receive");
        if (VCI_OpenDevice == NULL)
        {
            p = (char*)"VCI_OpenDevice";
            goto err;
        }
        if (VCI_CloseDevice == NULL)
        {
            p = (char*)"VCI_CloseDevice";
            goto err;
        }
        if (VCI_InitCAN == NULL)
        {
            p = (char*)"VCI_InitCAN";
            goto err;
        }
        if (VCI_StartCAN == NULL)
        {
            p = (char*)"VCI_StartCAN";
            goto err;
        }
        if (VCI_ClearBuffer == NULL)
        {
            p = (char*)"VCI_ClearBuffer";
            goto err;
        }
        if (VCI_Transmit == NULL)
        {
            p = (char*)"VCI_Transmit";
            goto err;
        }
        if (VCI_Receive == NULL)
        {
            p = (char*)"VCI_Receive";
            goto err;
        }
    }
    return STATUS_OK;
err:
    flag = 0;
    printf("init dll but err %s\n.", p);
    return STATUS_ERR;
#endif
    return STATUS_OK;
}

int can_box_ch_start(int devIndex, int canCH, int baundRate, int clearBuf=0)
{
    int ret;
    static VCI_INIT_CONFIG initConfig;

    memset(&initConfig, 0, sizeof(initConfig));

    initConfig.AccCode = 0x80000008;
    initConfig.AccMask = 0xFFFFFFFF;

    initConfig.Reserved = 0;

    initConfig.Filter = 0;  // 0或1：接收所有类型 滤波器同时对标准帧与扩展帧过滤
                            // 2     只接收标准帧 滤波器只对标准帧过滤，扩展帧将直接被滤除。
                            // 3     只接收扩展帧 滤波器只对扩展帧过滤，标准帧将直接被滤除。 

    switch (baundRate) {
    case 10:
        initConfig.Timing0 = 0x31;
        initConfig.Timing1 = 0x1c;
        break;
    case 20:
        initConfig.Timing0 = 0x18;
        initConfig.Timing1 = 0x1c;
        break;
    case 40:
        initConfig.Timing0 = 0x87;
        initConfig.Timing1 = 0xff;
        break;
    case 50:
        initConfig.Timing0 = 0x09;
        initConfig.Timing1 = 0x1c;
        break;
    case 80:
        initConfig.Timing0 = 0x83;
        initConfig.Timing1 = 0xff;
        break;
    case 100:
        initConfig.Timing0 = 0x04;
        initConfig.Timing1 = 0x1c;
        LogD("CAN :Setup baundRate 100 K");
        break;
    case 125:
        initConfig.Timing0 = 0x03;
        initConfig.Timing1 = 0x1c;
        LogD("CAN :Setup baundRate 125 K");
        break;
    case 200:
        initConfig.Timing0 = 0x81;
        initConfig.Timing1 = 0xfa;
        LogD("CAN :Setup baundRate 200 K");
        break;
    case 250:
        initConfig.Timing0 = 0x01;
        initConfig.Timing1 = 0x1c;
        LogD("CAN :Setup baundRate 250 K");
        break;
    case 400:
        initConfig.Timing0 = 0x80;
        initConfig.Timing1 = 0xfa;
        break;
    case 500:
        initConfig.Timing0 = 0x00;
        initConfig.Timing1 = 0x1c;
        LogD("CAN :Setup baundRate 500 K");
        break;
    case 666:
        initConfig.Timing0 = 0x80;
        initConfig.Timing1 = 0xb6;
        break;
    case 800:
        initConfig.Timing0 = 0x00;
        initConfig.Timing1 = 0x16;
        LogD("CAN :Setup baundRate 800 K");
        break;
    case 1000:
        initConfig.Timing0 = 0x00;
        initConfig.Timing1 = 0x14;
        LogD("CAN :Setup baundRate 1000 K");
        break;
    case 33:
        initConfig.Timing0 = 0x09;
        initConfig.Timing1 = 0x6f;
        LogD("CAN :Setup baundRate 33.33 K");
        break;
    case 66:
        initConfig.Timing0 = 0x04;
        initConfig.Timing1 = 0x6f;
        LogD("CAN :Setup baundRate 66.66 K");
        break;
    case 83:
        initConfig.Timing0 = 0x03;
        initConfig.Timing1 = 0x6f;
        LogD("CAN :Setup baundRate 83.33 K");
        break;
    default:
        break;
    }

    initConfig.Mode = 0;   // 正常工作
    ret = VCI_InitCAN(devtype, devIndex, canCH, &initConfig);//初始化设备通道
    if (STATUS_ERR == ret)
    {
        goto end;
    }
    Sleep(300);


    ret = VCI_StartCAN(devtype, devind, canCH);//通道开始采集
    if (STATUS_ERR == ret)
    {
        goto end;
    }
    // 清空指定CAN通道的缓冲区。主要用于需要清除接收缓冲区数据的情况,  缓冲区数据也会一并清除。
    if(clearBuf)
    {
        VCI_ClearBuffer(devtype, devind, canCH);
    }

end:
    return ret;
}

int can_box_ch_init(struct canConfig *config)
{
    int ret;
    int baundRate;
    int canCH;
    int clearBuff;

    devind = config->canBoxID;
    baundRate = config->baundRate;
    canCH = config->canCh;
    clearBuff = config->clearBuffWhenStart;

    ret = VCI_OpenDevice(VCI_USBCAN2, 0, 0);
    if (ret == STATUS_ERR)
    {
        goto end;
    }

    //LogD("VCI_OpenDevice : ", ret);
    Sleep(300);

    ret = can_box_ch_start(devind, canCH, baundRate, clearBuff);
    if (ret == STATUS_ERR)
    {
        goto end;
    }

    return STATUS_OK;
end:
    LogE("Setup but ERROR");
    VCI_CloseDevice(devtype, devind);
    return STATUS_ERR;
}

/*!
 *  @brief  发送数据到指定的通道
 *
 *  @param[in]  can_channel ： CAN 通道（一个CAN盒子有2个通道，一个1，一个2）
 *
 *  @param[in] ID ： CAN报文的ID
 *
 *  @param[in] data ： CAN报文的数据
 *  
 *  @param[in] sizec ： CAN报文的数据长度
 *  
 *  @return 成功返回0，失败返回负数
 *
 */
int can_box_send_data_to_channel(enum CAN_CHANNEL can_channel, unsigned int id, unsigned char* data, int sizec)
{
    int count = 1;
    int dwRel;
    VCI_CAN_OBJ vco[3];
    int i, j;

    for (i = 0; i < 1; i++)
    {
        vco[i].ID = id; //ID号//需要实际填写
        vco[i].RemoteFlag = 0;
        vco[i].ExternFlag = 0;
        vco[i].SendType = 1; // 0时为正常发送（发送失败会自动重发，重发时间为4秒，4秒内没有发出则取消）；
                             // =1时为单次发送（只发送一次，发送失败不会自动重发，总线只产生一帧数据）；
                             // 二次开发，建议SendType=1，提高发送的响应速度
        vco[i].DataLen = sizec;
        for (j = 0; j < sizec; j++)
            vco[i].Data[j] = data[j];
    }

    dwRel = VCI_Transmit(devtype, devind, can_channel, vco, count);
    if (dwRel <= 0)
    {
        LogE("CAN [%d] 通道发送错误\n" , can_channel + 1, dwRel);
        return dwRel;
    }
    return 0;
}

void can_box_send_data_to_channel1(unsigned int ID, unsigned char* data, int sizec)
{
    can_box_send_data_to_channel(CAN_CH1, ID, data, sizec);
}

void can_box_send_data_to_channel2(unsigned int ID, unsigned char* data, int sizec)
{
    can_box_send_data_to_channel(CAN_CH2, ID, data, sizec);
}

/*!
 *  @brief  从指定的通道接受数据
 *
 *  @param[in]  can_channel ： CAN 通道（一个CAN盒子有2个通道，一个1，一个2）
 *
 *  @param[out] ID ： 收到的帧ID
 *
 *  @param[out] data ： 收到的帧数据
 * 
 *  @param[out] sizec ： 收到的帧大小
 *    
 *  @return 返回收到的帧数，未收到时返回0
 *
 */
int can_box_recv_data_from_channel(enum CAN_CHANNEL can_channel, unsigned int *id, unsigned char* data, int *sizec)
{
    // 注意：数组的大小一定要比下面的len 参数大，否则会出现内存读写错误。  
    VCI_CAN_OBJ precv[3000] = { 0 };
    //int maxObjCount = 1; // 数据队列的最大长度
    int len = 100; // 2500
    int i;
    int Tr, retry;
    for(retry = 0 ; retry < 5; retry ++)
    {

        // 一般pReceive数 组大小与Len都设置大于2000，如：2500为宜，可有效防止数据溢出导致地址冲突。同时每隔30ms调用一次VCI_Receive为宜。 
        // （在满足应用的时效性情况下，尽量降低调用VCI_Receive 的频率，只要保证内部缓存不溢出，每次读取并处理更多帧，可以提高运行效率。）  
        // 返回实际读取的帧数
        Tr = VCI_Receive(devtype, devind, can_channel, precv, len, 0);
        if (Tr == 0)
        {
            //LogE("VCI_Receive failed");
            Sleep(250);
            continue;
        }
        if(id !=NULL)
        {
            *id = precv[0].ID;
        }
        if(sizec != NULL)
        {
            *sizec = precv[0].DataLen;
        }
        if(data != NULL)
        {
            for (i = 0; i < precv[0].DataLen; i++)
            {
                //printf("%x ", precv[0].Data[i]);
                data[i] = precv[0].Data[i];
            }
        }
        return Tr;
    }
    LogE("VCI_Receive failed");
    return 0;
}


int send_and_read_loop(void)
{
    int sendType = 1;
    int RemoteFlag = 0;
    int ExternFlag = 0;

    VCI_CAN_OBJ psend[1] = { 0 };
    VCI_CAN_OBJ precv[2500] = { 0 };
    int id = 0x11;
    int len = 8;
    //发送
    int Tr;
    int i;
    psend[0].ID = id;//ID号//需要实际填写//
    psend[0].SendType = sendType; // 0时为正常发送（发送失败会自动重发，重发时间为4秒，4秒内没有发出则取消）；
                        // =1时为单次发送（只发送一次，发送失败不会自动重发，总线只产生一帧数据）；
                        // 二次开发，建议SendType=1，提高发送的响应速度
    psend[0].RemoteFlag = RemoteFlag;
    psend[0].ExternFlag = ExternFlag;
    psend[0].DataLen = len;
    for (int i = 0; i < len; i++)
    {
        psend[0].Data[i] = 0xf0 + i;
    }
    while(1)
    {
#if 1
        //VCI_ClearBuffer(devtype, devind, CAN_CH1);
        Sleep(100);
        psend[0].ID = id;//ID号//需要实际填写//
        psend[0].SendType = sendType; // 0时为正常发送（发送失败会自动重发，重发时间为4秒，4秒内没有发出则取消）；
                            // =1时为单次发送（只发送一次，发送失败不会自动重发，总线只产生一帧数据）；
                            // 二次开发，建议SendType=1，提高发送的响应速度
        psend[0].RemoteFlag = RemoteFlag;
        psend[0].ExternFlag = ExternFlag;
        psend[0].DataLen = len;
        for (int i = 0; i < len; i++)
        {
            psend[0].Data[i] = 'a' + i;
        }

        Tr = VCI_Transmit(devtype, devind, CAN_CH1, psend, 1);
        //devtype设备类型号//devind设备索引号//canind第几路CAN//&psend要发送的数据帧数组的首指针//1要发送的数据帧数组的长度//
        if (Tr == STATUS_ERR)
        {
            LogE("send failed");
            return STATUS_ERR;
        }
        Sleep(100);
#endif
        Tr = VCI_Receive(devtype, devind, CAN_CH1, precv, 100, 0); //2500, 0);
        if (Tr == STATUS_ERR)
        {
            LogE("VCI_Receive failed");
            Sleep(100);
            continue;
        }
        printf("id is %x\n", precv[0].ID);
        printf("DataLen is %d\n", precv[0].DataLen);
        for (i = 0; i < precv[0].DataLen; i++)
        {
            printf("%x ", precv[0].Data[i]);
        }
        printf("\n");
        //break;
    }
    LogD("--");
    return STATUS_OK;
}


void deinit_dll(void)
{
#if USING_MSVC_BUILD
    //LogD("deinit_dll : ");
    if (VCI_CloseDevice != NULL)
    {
        //LogD("VCI_CloseDevice : ");
        VCI_CloseDevice(VCI_USBCAN2, devind);
        //Sleep(300);
    }
#else
    VCI_CloseDevice(VCI_USBCAN2, devind);
#endif
    //LogD("FreeLibrary : ");
#if USING_MSVC_BUILD
    if (module != NULL)
        FreeLibrary(module);
#endif
}
